package cn.com.chengmandian.bike.service.impl;

import cn.com.chengmandian.agent.utils.AuthUtil;
import cn.com.chengmandian.bike.dto.CreateWxPayScoreDto;
import cn.com.chengmandian.bike.dto.WxConfirmPayScoreDto;
import cn.com.chengmandian.bike.model.EbMemberPay;
import cn.com.chengmandian.bike.model.EbPreOrder;
import cn.com.chengmandian.bike.mapper.EbPreOrderMapper;
import cn.com.chengmandian.bike.model.EbPriceRule;
import cn.com.chengmandian.bike.model.EbRideOrder;
import cn.com.chengmandian.bike.service.*;
import cn.com.chengmandian.core.common.utils.BillNoUtil;
import cn.com.chengmandian.core.redis.cache.AgentParamCacheService;
import cn.com.chengmandian.core.redis.service.RedisService;
import cn.com.chengmandian.core.web.pojo.AgentInfo;
import cn.com.chengmandian.core.web.pojo.AjaxResult;
import cn.com.chengmandian.core.web.pojo.BikeParam;
import cn.com.chengmandian.core.web.utils.LocalMapUtil;
import cn.com.chengmandian.service.bike.constants.MemberPayBusinessTypeEnum;
import cn.com.chengmandian.service.bike.constants.MemberPayStatusEnum;
import cn.com.chengmandian.service.bike.constants.PayChannelEnum;
import cn.com.chengmandian.service.bike.constants.PreOrderStatusEnum;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.payscore.*;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.PayScoreService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.PayScoreServiceImpl;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import static cn.com.chengmandian.bike.utils.WxPayUtil.getSerialNumber;

/**
 * <p>
 * 电单车运营-预支付-订单信息 服务实现类
 * </p>
 *
 * @author AutoGenerator
 * @since 2024-05-31
 */
@Slf4j
@Service
public class EbPreOrderServiceImpl extends ServiceImpl<EbPreOrderMapper, EbPreOrder> implements IEbPreOrderService {
    @Autowired
    private IEbRideOrderService rideOrderService;

    @Autowired
    private IEbMemberPayService memberPayService;

    @Autowired
    private AgentParamCacheService agentParamCacheService;

    @Autowired
    private IEbPriceRuleService priceRuleService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private IEbCommonParamService paramService;

    /**
     * 获取用户可使用的支付分订单
     * @param memberId
     * @return
     */
    @Override
    public EbPreOrder getNotUsePayScore(Integer memberId) {
        return this.getOne(new LambdaQueryWrapper<EbPreOrder>()
                .ge(EbPreOrder::getExpireTime, new Date())
                .eq(EbPreOrder::getStatus, PreOrderStatusEnum.PROCESS.getCode())
                .eq(EbPreOrder::getMemberId, memberId
                ).last("limit 1"));

    }

    /**
     * 取消用户支付分订单
     * @param memberId
     */
    @Override
    public void cancelOrder(Integer memberId) {
        EbPreOrder preOrder = this.getNotUsePayScore(memberId);
        if (preOrder == null) return;
        this.cancelOrder(preOrder);
    }

    @Override
    public void cancelOrderById(Integer preOrderId) {
        EbPreOrder preOrder = this.getById(preOrderId);
        if (preOrder == null) return;
        this.cancelOrder(preOrder);
    }

    /**
     * 根据骑行订单取消微信支付分订单
     * @param rideOrder
     */
    @Override
    public void cancelOrderByRideOrder(EbRideOrder rideOrder) {
        EbMemberPay memberPay = memberPayService.getById(rideOrder.getPayId());
        if (memberPay == null) return;
        if (!memberPay.getPayChannel().equals(PayChannelEnum.WXPAYSCORE.getCode())) return;
        if (!memberPay.getStatus().equals(MemberPayStatusEnum.READY.getCode())) return;
        EbPreOrder preOrder = this.getOne(new LambdaQueryWrapper<EbPreOrder>()
                .eq(EbPreOrder::getPayId, memberPay.getId())
                .last("limit 1"));
        if (preOrder == null) return;
        this.cancelOrder(preOrder);
    }

    @Override
    public void updateOrderMoneyByRideId(Integer rideId) throws Exception {
        //更新支付分金额
        EbRideOrder rideOrder = rideOrderService.getById(rideId);
        Assert.notNull(rideOrder,"骑行订单ID传入有误");

        // 找到历史自动支付记录
        EbMemberPay memberPay = memberPayService.getById(rideOrder.getPayId());
        Assert.notNull(memberPay, "找不到支付记录：" + rideOrder.getPayId());

        Assert.isTrue(memberPay.getPayChannel().equals(PayChannelEnum.WXPAYSCORE.getCode()), "非支付分支付记录：" + memberPay.getPayNo());

        Assert.isTrue(memberPay.getStatus().equals(MemberPayStatusEnum.READY.getCode()), "支付记录不是待支付状态：" + memberPay.getStatus());

        EbPreOrder preOrder = this.getOne(new LambdaQueryWrapper<EbPreOrder>()
                .eq(EbPreOrder::getPayId, memberPay.getId())
                .last("limit 1"));
        Assert.notNull(preOrder, "找不到支付分订单：" + memberPay.getPayNo());

        Assert.isTrue(preOrder.getStatus().equals(PreOrderStatusEnum.PROCESS.getCode()), "支付分订单不可用：" + preOrder.getStatus());

        // 支付分金额
        BigDecimal payMoney = rideOrder.getOrderMoney();
        // 取消支付分订单
        if (payMoney.compareTo(BigDecimal.ZERO) <= 0) {
            this.cancelOrder(preOrder);
            // 取消支付订单
            memberPayService.update(new LambdaUpdateWrapper<EbMemberPay>()
                    .set(EbMemberPay::getStatus, MemberPayStatusEnum.CANCEL.getCode())
                    .eq(EbMemberPay::getId, memberPay.getId()));
            return;
        }

        // 金额一致，无需修改
        if (payMoney.compareTo(preOrder.getPayMoney()) == 0) return;

        WxPayService payService = new WxPayServiceImpl();
        //调用初始化config接口
        WxPayConfig payConfig = initialization();
        payService.setConfig(payConfig);
        PayScoreService payScoreService = new PayScoreServiceImpl(payService);
        WxPayScoreRequest request = new WxPayScoreRequest();
        request.setOutOrderNo(preOrder.getPreNo());
        request.setTotalAmount(payMoney.multiply(BigDecimal.valueOf(100)).intValue()); // 使用新的骑行金额
        List<PostPayment> payment = payment(rideOrder.getAreaId());
        request.setPostPayments(payment);
        request.setReason("用户投诉");
        log.info("request参数:{}", JSONObject.toJSONString(request));
        WxPayScoreResult response = payScoreService.modifyServiceOrder(request);
        log.info("修改订单金额响应 {}", JSONObject.toJSONString(response));

        // 更新支付金额
        memberPayService.update(new LambdaUpdateWrapper<EbMemberPay>()
                .set(EbMemberPay::getMoney, payMoney)
                .eq(EbMemberPay::getId, memberPay.getId()));

        this.update(new LambdaUpdateWrapper<EbPreOrder>()
                .set(EbPreOrder::getPayMoney, payMoney)
                .eq(EbPreOrder::getId, preOrder.getId()));
    }

    /**
     * 执行取消订单
     * @param preOrder
     */
    private void cancelOrder(EbPreOrder preOrder) {
        try{
            WxPayService payService = new WxPayServiceImpl();
            //调用初始化config接口
            WxPayConfig payConfig = initialization();
            payService.setConfig(payConfig);
            PayScoreService payScoreService = new PayScoreServiceImpl(payService);
            WxPayScoreResult response = payScoreService.cancelServiceOrder(preOrder.getPreNo(), "自行支付");
            log.info("取消支付分记录响应 {}", JSONObject.toJSONString(response));
        }catch(Exception e){
            log.error(e.getMessage());
        }

        this.update(new LambdaUpdateWrapper<EbPreOrder>()
                .set(EbPreOrder::getStatus, PreOrderStatusEnum.CANCEL.getCode())
                .eq(EbPreOrder::getPreNo, preOrder.getPreNo()));
    }

    /**
     * 获取支付中记录（20秒内开始支付的记录）
     * @param payId
     * @return
     */
    @Override
    public EbPreOrder getPayIng(Integer payId) {
        return this.getOne(new LambdaQueryWrapper<EbPreOrder>()
                .eq(EbPreOrder::getPayId, payId)
                .gt(EbPreOrder::getPayTime, DateUtil.offsetSecond(new Date(), -20)));
    }

    /**
     * 新增订单成功
     */
    @Override
    public void addOrderSuccess(String preNo, String orderId) {
        //修改预支付表的状态和过期时间（30天后）
        this.update(new LambdaUpdateWrapper<EbPreOrder>()
                .set(EbPreOrder::getStatus, PreOrderStatusEnum.PROCESS.getCode())
                .set(EbPreOrder::getExpireTime, DateUtil.offsetSecond(new Date(), 3600 * 24 * 30))
                .set(EbPreOrder::getChannelNo, orderId)
                .eq(EbPreOrder::getPreNo,preNo));
    }

    /**
     * 新增订单
     * @param dto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult addOrder(CreateWxPayScoreDto dto){
        String key = "CMD:BIKE:USER:PREORDER:ADD:" + AuthUtil.getAgentUser().getId();
        Assert.isTrue(!redisService.hasKey(key), "请稍后操作");
        redisService.set(key,true,3);
        try{
            //判断订单是否有效
            EbPreOrder preOrder = getNotUsePayScore(AuthUtil.getAgentUser().getId());

            WxConfirmPayScoreDto confirmPayScoreDto = new WxConfirmPayScoreDto();
            if (preOrder != null) {
                WxPayService payService = new WxPayServiceImpl();
                //调用初始化config接口
                WxPayConfig payConfig = initialization();
                payService.setConfig(payConfig);
                PayScoreService payScoreService = new PayScoreServiceImpl(payService);
                //查询支付分订单（暂定已创单和订单进行为订单有效）
                WxPayScoreResult response = payScoreService.queryServiceOrder(preOrder.getPreNo(), "");
                log.info("查询支付分订单响应 {}", response);
                if (("CREATED").equals(response.getState()) || ("DOING").equals(response.getState())) {
                    confirmPayScoreDto.setPayNo(preOrder.getPreNo());
                    return AjaxResult.error(520,"有效订单", confirmPayScoreDto);
                }
            }
            WxPayScoreRequest request = new WxPayScoreRequest();
            WxPayService payService = new WxPayServiceImpl();
            //调用初始化config接口
            WxPayConfig payConfig = initialization();
            payService.setConfig(payConfig);
            PayScoreService payScoreService = new PayScoreServiceImpl(payService);
            if(dto.getNeedUserConfirm() == null) dto.setNeedUserConfirm(false);
            if (!dto.getNeedUserConfirm()) {
                try{
                    //商户查询与用户的授权记录
                    WxPayScoreResult response = payScoreService.permissionsQueryByOpenId(dto.getOpenId());
                    request.setOpenid(dto.getOpenId());
                    log.info("查询用户授权记录响应 {}", response);
                    if ("UNAVAILABLE".equals(response.getAuthorizationState())) {
                        return AjaxResult.error(503,"用户未授权服务");
                    } else if ("UNBINDUSER".equals(response.getAuthorizationState())) {
                        return AjaxResult.error(504,"未绑定用户");
                    }
                }catch(Exception e){
                    return AjaxResult.error(503,e.getMessage());
                }
            }
            //创建预支付数据
            EbPreOrder ebPreOrder = new EbPreOrder();
            ebPreOrder.setMemberId(AuthUtil.getAgentUser().getId());
            ebPreOrder.setAgentId(AuthUtil.getAgentUser().getAgentId());
            ebPreOrder.setPreNo(BillNoUtil.getSimpleNo("PO-"));
            ebPreOrder.setStatus(PreOrderStatusEnum.READY.getCode());
            ebPreOrder.setChannel(1);
            ebPreOrder.setPreMoney(BigDecimal.valueOf(dto.getAmount()));
            ebPreOrder.setPreTime(new Date());
            this.save(ebPreOrder);

            RiskFund riskFund = new RiskFund();
            riskFund.setName("ADVANCE");
            riskFund.setAmount(dto.getAmount() * 100);//元转化为分

            List<PostPayment> payment = payment(LocalMapUtil.getAgentInfo().getAreaId());
            request.setPostPayments(payment);
            TimeRange timeRange = new TimeRange();
            timeRange.setStartTime("OnAccept");
            request.setTimeRange(timeRange);
            request.setRiskFund(riskFund);
            request.setServiceIntroduction("创建支付分订单");
            request.setOutOrderNo(ebPreOrder.getPreNo());
            request.setNotifyUrl("https://" + LocalMapUtil.getAgentInfo().getDomain() + "/api/v1/bikeuser/notify" + "/wx/pay/score/create/service/" + LocalMapUtil.getAgentInfo().getAgentNo());
            request.setNeedUserConfirm(dto.getNeedUserConfirm());
            //创建支付分订单
            WxPayScoreResult serviceOrder = payScoreService.createServiceOrder(request);
            log.info("参数：{}", JSONObject.toJSONString(serviceOrder));
            confirmPayScoreDto.setPayNo(ebPreOrder.getPreNo());
            //免确认模式
            if(request.getNeedUserConfirm() == false && serviceOrder.getState().equals("DOING")){
                return AjaxResult.success(confirmPayScoreDto);
            }
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return AjaxResult.error(505,"创建支付分订单失败");
        }catch(Exception e){
            redisService.del(key);
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return AjaxResult.error(505,"创建支付分订单失败"+e.getMessage());
        }
    }

    /**
     * 预授权
     * @return
     */
    @Override
    public AjaxResult preAuthorization() {
        try {
            //引导用户预授权跳转
            WxPayService payService = new WxPayServiceImpl();
            //调用初始化config接口
            WxPayConfig payConfig = initialization();
            payService.setConfig(payConfig);
            PayScoreService payScoreService = new PayScoreServiceImpl(payService);
            WxPayScoreRequest wxPayScoreRequest = new WxPayScoreRequest();
            wxPayScoreRequest.setAuthorizationCode(UUID.randomUUID().toString().substring(0,32));
            wxPayScoreRequest.setNotifyUrl("https://" + LocalMapUtil.getAgentInfo().getDomain() + "/api/v1/bikeuser/notify" + "/wx/isPermissions/" + LocalMapUtil.getAgentInfo().getAgentNo());
            //商户预授权
            return AjaxResult.success(payScoreService.permissions(wxPayScoreRequest));
        } catch (Exception e) {
            return AjaxResult.error("预授权失败:"+e.getMessage());
        }
    }

    /**
     * 查询下单结果
     * @param payNo
     * @return
     */
    @Override
    public AjaxResult preAuthorizationSelect(String payNo) {
        EbPreOrder preOrder = this.getOne(new LambdaQueryWrapper<EbPreOrder>()
                .eq(EbPreOrder::getPreNo, payNo)
                .last("limit 1"));
        if (preOrder != null && preOrder.getPreTime() != null) {
            if (new Date().getTime() - preOrder.getPreTime().getTime() < 3 * 60 * 1000 && preOrder.getStatus() != PreOrderStatusEnum.PROCESS.getCode()) {
                return AjaxResult.error(502,"重新调用");
            } else if (new Date().getTime() - preOrder.getPreTime().getTime() <= 3 * 60 * 1000 && preOrder.getStatus() == PreOrderStatusEnum.PROCESS.getCode()) {
                return AjaxResult.success("操作成功");
            }
        }
        return AjaxResult.error("结束调用");
    }


    /**
     * 为骑行订单支付
     * @param rideOrder
     * @return
     */
    @Override
    public boolean payForRideOrder(EbRideOrder rideOrder) {
        try {
            EbPreOrder preOrder = this.getNotUsePayScore(rideOrder.getMemberId());
            if(preOrder == null) return false;
            BigDecimal payMoney = rideOrder.getOrderMoney();
            // 开始支付
            EbMemberPay memberPay = memberPayService.createPay(rideOrder.getAreaId(),
                    rideOrder.getMemberId(),
                    MemberPayBusinessTypeEnum.RIDE_ORDER_PAY,
                    rideOrder.getId(),
                    payMoney,
                    PayChannelEnum.WXPAYSCORE.getCode());
            // 预支付订单更新
            this.update(new LambdaUpdateWrapper<EbPreOrder>()
                    .set(EbPreOrder::getPayMoney, payMoney)
                    .set(EbPreOrder::getPayTime, new Date())
                    .set(EbPreOrder::getPayId, memberPay.getId())
                    .eq(EbPreOrder::getId, preOrder.getId()));

            // 支付记录关联订单
            rideOrderService.update(new LambdaUpdateWrapper<EbRideOrder>()
                    .set(EbRideOrder::getPayNo, memberPay.getPayNo())
                    .set(EbRideOrder::getPayId, memberPay.getId())
                    .eq(EbRideOrder::getId, rideOrder.getId()));

            try {
                WxPayService payService = new WxPayServiceImpl();
                //调用初始化config接口
                WxPayConfig payConfig = initialization();
                payService.setConfig(payConfig);
                PayScoreService payScoreService = new PayScoreServiceImpl(payService);
                WxPayScoreRequest request = new WxPayScoreRequest();
                TimeRange timeRange = new TimeRange();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
                //结束时间要在创建订单时间之后，调用接口之前（暂定为当前时间的前5秒钟）
                timeRange.setEndTime(sdf.format(new Date(new Date().getTime()-5 * 1000)));
                //传入的元转化为分
                request.setTotalAmount(payMoney.multiply(BigDecimal.valueOf(100)).intValue());
                request.setTimeRange(timeRange);
                request.setOutOrderNo(preOrder.getPreNo());
                request.setProfitSharing(true);
                List<PostPayment> payment = payment(rideOrder.getAreaId());
                request.setPostPayments(payment);
                log.info("request参数:{}", JSONObject.toJSONString(request));
                //完结支付分订单后会自动发起免密代扣(回调地址和创建订单回调地址相同)
                WxPayScoreResult wxPayScoreResult = payScoreService.completeServiceOrder(request);
                log.info("完结支付分订单信息:" + JSONObject.toJSONString(wxPayScoreResult));
                return true;
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return false;
    }

    /**
     * 信用分支付成功
     * @param preNo
     * @param channelNo
     */
    @Override
    public void paySuccessByNo(String preNo, String channelNo) {
        // 20240608 临时代码
        if (preNo.startsWith("MP-")) {
            EbMemberPay memberPay = memberPayService.getByNo(preNo);
            Assert.notNull(memberPay, "支付信息不存在：" + preNo);
            memberPayService.paySuccessByPay(memberPay, channelNo);
            return;
        }
        EbPreOrder preOrder = this.getOne(new LambdaQueryWrapper<EbPreOrder>()
                .eq(EbPreOrder::getPreNo, preNo)
                .last("limit 1"));
        Assert.notNull(preOrder, "预支付订单不存在：" + preNo);

        EbMemberPay memberPay = memberPayService.getById(preOrder.getPayId());
        Assert.notNull(memberPay, "支付信息不存在：" + preOrder.getPayId());
        Assert.isTrue(memberPay.getStatus().equals(MemberPayStatusEnum.READY.getCode()), "支付状态不正确:" + memberPay.getPayNo());

        memberPayService.paySuccessByPay(memberPay, channelNo);

        //如果是微信支付分则修改预支付表
        this.update(new LambdaUpdateWrapper<EbPreOrder>()
                .set(EbPreOrder::getStatus, PreOrderStatusEnum.COMPLETE.getCode())
                .set(EbPreOrder::getPayTime, new Date())
                .eq(EbPreOrder::getId, preOrder.getId()));
    }

    private WxPayConfig initialization(){
        AgentInfo agentInfo = LocalMapUtil.getAgentInfo();
        BikeParam param = agentParamCacheService.getParam(agentInfo.getId());
        //初始化configparam配置
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setMchId(agentInfo.getWxMchId());
        payConfig.setMchKey(agentInfo.getWxApiKey3());
        payConfig.setAppId(agentInfo.getWxMiniAppid());
        payConfig.setServiceId(param.getServiceId());
        payConfig.setApiV3Key(agentInfo.getWxApiKey3());
        payConfig.setPrivateKeyPath(agentInfo.getWxKeyPath());
        payConfig.setPrivateCertPath(agentInfo.getWxCertPath());
        String serialNumber = getSerialNumber(agentInfo.getWxCertPath());
        payConfig.setCertSerialNo(serialNumber);
        log.info("config配置:"+payConfig);
        // 可以指定是否使用沙箱环境
        payConfig.setUseSandboxEnv(false);
        return payConfig;
    }

    private List<PostPayment> payment(Integer areaId){
        EbPriceRule rule = priceRuleService.getOne(new LambdaQueryWrapper<EbPriceRule>().eq(EbPriceRule::getAreaId, areaId).last("limit 1"));
        List<PostPayment> list = new ArrayList();
        PostPayment postPayment = new PostPayment();
        postPayment.setDescription("起步价"+rule.getBeforeMinuteFee()+"元/"+rule.getBeforeMinute()+"分钟,超时后每"+rule.getAfterMinute()+"分钟收取"+rule.getAfterMinuteFee()+"元");
        postPayment.setName("计费规则");
        list.add(postPayment);
        return list;
    }
}
